using System.Collections.Generic;
using NFreeMarker.Template;

namespace NFreeMarker.Core
{
    /**
     * Add sequence capabilities to an existing collection, or
     * vice versa. Used by ?keys and ?values built-ins.
     */
    public sealed class CollectionAndSequence : ITemplateCollectionModel, ITemplateSequenceModel //, Serializable
    {
        private readonly ITemplateCollectionModel _collection;
        private readonly ITemplateSequenceModel _sequence;
        private List<ITemplateModel> _data;

        public CollectionAndSequence(ITemplateCollectionModel collection)
        {
            _collection = collection;
        }

        public CollectionAndSequence(ITemplateSequenceModel sequence)
        {
            _sequence = sequence;
        }

        public ITemplateModelIterator Iterator()
        {
            return _collection != null
                ? _collection.Iterator()
                : new SequenceIterator(_sequence);
        }

        public ITemplateModel Get(int i)
        {
            if (_sequence != null)
            {
                return _sequence.Get(i);
            }

            InitSequence();
            return _data[i];
        }

        public int Size()
        {
            if (_sequence != null)
            {
                return _sequence.Size();
            }
            
            InitSequence();
            return _data.Count;
        }

        private void InitSequence()
        {
            if (_data != null) return;

            _data = new List<ITemplateModel>();
            ITemplateModelIterator it = _collection.Iterator();
            while (it.HasNext())
            {
                _data.Add(it.Next());
            }
        }

        private class SequenceIterator : ITemplateModelIterator
        {
            private readonly ITemplateSequenceModel _sequence;
            private readonly int _size;
            private int _index;

            public SequenceIterator(ITemplateSequenceModel sequence)
            {
                _sequence = sequence;
                _size = sequence.Size();
            }

            public ITemplateModel Next()
            {
                return _sequence.Get(_index++);
            }

            public bool HasNext()
            {
                return _index < _size;
            }
        }
    }
}